﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace CalculatorLogic
{
    /// <summary>
    /// An object that will allow you to find the List of (x,y) points that solve Y= equations
    /// </summary>
    public class GraphSolutionFinder
    {

        #region fields

        /// <summary>
        /// the current equation to evaluate
        /// </summary>
        string equation;

        /// <summary>
        /// the current Graph Settings
        /// </summary>
        GraphSetting graphSetting;

        //probably won't be used
        /// <summary>
        /// the values for X to be checked
        /// </summary>
        List<Double> xValues;

        /// <summary>
        /// the number of solutions found
        /// </summary>
        int solutionPointCount;

        /// <summary>
        /// the PostFixSolver for the GraphSolutionFinder
        /// </summary>
        PostfixSolver solver;

        /// <summary>
        /// the EquationParser for the GraphSolutionFinder
        /// </summary>
        EquationParser equationParser;

        /// <summary>
        /// The number of solutions found after calling FindSolutionPoints()
        /// </summary>
        public int SolutionPointCount
        {
            get { return solutionPointCount; }
        }


        #endregion fields

        #region Properties

        /// <summary>
        /// Get the current string equation
        /// </summary>
        public string Equation
        {
            get { return equation; }

        }

        public GraphSetting GraphSetting
        {
            get { return graphSetting; }
        }

        public List<Double> XValues
        {
            get { return xValues; }
            //I may remove the setter, not sure yet
            set { xValues = value; }
        }


        #endregion Properties

        #region Constructors
        /// <summary>
        /// empty GraphSolutionFinder Constructor, initializes Solver and EquationParser
        /// </summary>
        public GraphSolutionFinder()
        {
            //the empty constructor should probably be removed, we should pass some kind of string at all times
            solver = new PostfixSolver();
            equationParser = new EquationParser();
        }

        /// <summary>
        /// Constructor with string equation param, to store in private equation member, initializes Solver and EquationParser
        /// </summary>
        /// <param name="equation">the currect equation to evaluate</param>
        public GraphSolutionFinder(string equation)
        {
            this.equation = equation;
            solver = new PostfixSolver();
            equationParser = new EquationParser();
        }

        #endregion Constructors

        #region PublicMethods

        public PointCollection FindSolutionPoints(string equation, GraphSetting graphSettings)
        {
            MatchCollection matches = Regex.Matches(equation, @"\dx");
            foreach (Match match in matches)
            {
                equation = equation.Replace(match.Value, match.Value.Remove(1) + " * " + match.Value.Remove(0, 1));
            }
            //replace the value of x in the equation
            Equation equationToFind = new Equation();

            Point newSolutionPoint;
            //create a list of doubles
            List<Double> xValues = new List<Double>();
            //create a list of Points
            PointCollection solutionPoints = new PointCollection();

            //assign modified equation to the Equation object
            equationToFind.Input[0] = equation;

            //get the list of xvalues
            xValues = buildVariableList(graphSettings);

            //for the list and call findSolutionPoint()
            for (int i = 0; i < xValues.Count; i++)
            {
                newSolutionPoint = findSolutionPoint(equation.Replace("x", xValues[i].ToString()), xValues[i],equationToFind);
                solutionPoints.Add(newSolutionPoint);
            }

            this.solutionPointCount = solutionPoints.Count;

            //return a list of points that satisfy this equation
            return solutionPoints;
        }

        /// <summary>
        /// Build a list of X values
        /// </summary>
        /// <param name="settings">the current GraphSettings struct to use</param>
        /// <returns>A list of X Values as Double</returns>
        public List<Double> BuildVariableList(GraphSetting settings)
        {
            List<Double> tempDoubles = new List<double>();

            tempDoubles = buildVariableList(settings);

            //I might need to make sure that they are sorted lowest to highest
            tempDoubles.Sort();

            return tempDoubles;
        }


        #endregion PublicMethods

        #region privateMethods

        private Point findSolutionPoint(string equation, double xValue, Equation values)
        {
            Point tempPoint; 

            double yValue = 0.0d;

            string returnValue;

            string solution;

            returnValue = equationParser.Parse(equation);

            solution = solver.Solve(returnValue,values);

            yValue = Convert.ToDouble(solution);

            tempPoint = new Point(xValue, yValue);

            //return a point that satisfies this equation
            return tempPoint;
        }

        private List<Double> buildVariableList(GraphSetting graphSetting)
        {
            double xScale = 0.0d;
            double xMax = 0.0d;
            double xMin = 0.0d;

            xScale = graphSetting.XScale;
            xMin = graphSetting.XMin;
            xMax = graphSetting.XMax;
            //I added this step value
            double step = (xMax - xMin) / 100;

            List<Double> doubles = new List<Double>();

            //I changed the for loop to loop from xMin to xMax by the step value
            for (double i = xMin; i <= xMax; i += step)
            {
                //double xValue = 0.0d;

                //xValue = xMin += (xScale * i);
                
                //just adding i to the list
                doubles.Add(i);
            }

            return doubles;
        }

        #endregion privateMethods

    }
}
